﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Newtonsoft.Json;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using System.Linq.Expressions;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using System.IO;

namespace Ry.OpcUa.ClientDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        SynchronizationContext synchronizationContext = SynchronizationContext.Current;

        Session session;
        public async Task ConnectToServerAsync(string endpointUrl)
        {
            var selectedEndpoint = CoreClientUtils.SelectEndpoint(endpointUrl, false, 15000);

            var application = new ApplicationInstance
            {
                ApplicationName = "MyOPCClient",
                ApplicationType = ApplicationType.Client,
                ConfigSectionName = "OpcUaClient",

            };

            // 手动创建ApplicationConfiguration对象 
            application.ApplicationConfiguration = new Opc.Ua.ApplicationConfiguration
            {
                ApplicationName = "MyOPCClient",
                ApplicationType = ApplicationType.Client,
                ApplicationUri = "urn:MyOPCClient",
                ProductUri = "http://MyOPCClient",
                SecurityConfiguration = new SecurityConfiguration
                {
                    ApplicationCertificate = new CertificateIdentifier
                    {
                        StoreType = "Directory",
                        StorePath = "CertificateStores/MachineDefault",
                        SubjectName = "CN=MyOPCClient"
                    },
                    TrustedIssuerCertificates = new CertificateTrustList
                    {
                        StoreType = "Directory",
                        StorePath = "CertificateStores/UA Applications"
                    },
                    TrustedPeerCertificates = new CertificateTrustList
                    {
                        StoreType = "Directory",
                        StorePath = "CertificateStores/UA Applications"
                    },
                    RejectedCertificateStore = new CertificateTrustList
                    {
                        StoreType = "Directory",
                        StorePath = "CertificateStores/RejectedCertificates"
                    }
                },
                TransportConfigurations = new TransportConfigurationCollection(),
                TransportQuotas = new TransportQuotas
                {
                    OperationTimeout = 60000,
                    MaxStringLength = 1048576,
                    MaxByteStringLength = 1048576,
                    MaxArrayLength = 65535,
                    MaxMessageSize = 4194304,
                    MaxBufferSize = 65535,
                    ChannelLifetime = 300000,
                    SecurityTokenLifetime = 3600000
                },
                ClientConfiguration = new ClientConfiguration
                {
                    DefaultSessionTimeout = 60000,
                    MinSubscriptionLifetime = 10000
                }
            };
            await application.CheckApplicationInstanceCertificate(false, 0).ConfigureAwait(true);

            var endpointConfiguration = EndpointConfiguration.Create(application.ApplicationConfiguration);
            var endpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration);
            session = await Session.Create(
                application.ApplicationConfiguration,
                endpoint,
                false,
                false,
                application.ApplicationName,
                60000,
                new UserIdentity(new AnonymousIdentityToken()),
                null).ConfigureAwait(false);

            Debug.WriteLine("Connected to OPC UA server.");
        }

        async Task<List<NodeId>> BrowseNode(NodeId nodeId, TreeNode parent)
        {
            var r = new List<NodeId>();
            var childBrowseDescription = new BrowseDescription
            {
                NodeId = nodeId,
                BrowseDirection = BrowseDirection.Forward,
                ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences,
                IncludeSubtypes = true,
                NodeClassMask = (uint)NodeClass.Object | (uint)NodeClass.Variable,
                ResultMask = (uint)BrowseResultMask.All
            };

            var childBrowseResult = session.Browse(null, null, 0,
                new[] { childBrowseDescription },
                out var results,
                out var childDiagnosticInfos);

            foreach (var result in results)
            {
                foreach (var reference in result.References)
                {
                    r.Add(new NodeId(reference.NodeId.ToString()));
                    var thisNode = new TreeNode($"{reference.DisplayName.Text}【{reference.NodeId.ToString()}】");
                    thisNode.Name = reference.NodeId.ToString();
                    parent?.Nodes.Add(thisNode);

                    //子节点
                    //Debug.WriteLine($"Node ID: {reference.NodeId},TypeId:{reference.TypeId},TypeId:{reference.TypeDefinition}  Display Name: {reference.DisplayName.Text}");
                    var rr = await BrowseNode(NodeId.Parse(reference.NodeId.ToString()), parent == null ? null : thisNode).ConfigureAwait(false);
                    r.AddRange(rr);
                }
            }
            return r;
        }

        private async Task ReadNodes(List<NodeId> nbs, Action<DataValue, int> action)
        {
            var response = await session.ReadAsync(null, 0, TimestampsToReturn.Both,
                new ReadValueIdCollection(nbs.Select(ni => new ReadValueId { NodeId = ni, AttributeId = Attributes.Value })),
                default);
            for (int i = 0; i < response.Results.Count; i++)
            {
                var result = response.Results[i];
                action?.BeginInvoke(result, i, null, null);
                await Task.Delay(1);
            }
        }

        private void 连接ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                ConnectToServerAsync(toolStripTextBox1.Text).Wait();
                连接ToolStripMenuItem.Enabled = false;
                断开ToolStripMenuItem.Enabled = true;
                treeView1.Nodes.Add(toolStripTextBox1.Text);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
        private void 断开ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            session.Close();
            treeView1.Nodes.Clear();
            连接ToolStripMenuItem.Enabled = true;
            断开ToolStripMenuItem.Enabled = false;
        }

        private async void 扫描节点ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                synchronizationContext = SynchronizationContext.Current;
                treeView1.TopNode.Nodes.Clear();
                treeView1.ShowNodeToolTips = true;
                var nbs = await BrowseNode(ObjectIds.RootFolder, treeView1.TopNode);
                if (nbs.Count > 0)
                {
                    await ReadNodes(nbs, (result, i) =>
                    {
                        synchronizationContext.Post(state =>
                        {
                            var treenode = treeView1.TopNode.Nodes.Find(nbs[i].ToString(), true).FirstOrDefault();
                            if (treenode != null)
                            {
                                treenode.Text += result.Value == null ? "" : $"\t{result.Value?.GetType().Name}";
                                treenode.Tag = result.Value?.GetType();

                                if (StatusCode.IsGood(result.StatusCode))
                                {
                                    treenode.ToolTipText = JsonConvert.SerializeObject(result.Value);
                                    //if (result.Value is Array arr)
                                    //{
                                    //    treenode.ToolTipText = "value："
                                    //        + string.Join(",", arr.Cast<object>().Select(ar => ar.ToString()));
                                    //}
                                    //else
                                    //    treenode.ToolTipText = "value：" + $"{result.Value}";
                                }
                                else
                                {
                                    treenode.ToolTipText = "无值";
                                }
                            }
                        }, null);
                    });
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private async void 读取ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                var nn = treeView1.SelectedNode.Name;
                //nn= "ns=2;s=Devices/分厂一/车间一/测试空设备/温度";
                var list = new[] { nn }.Select(ni => new NodeId(ni)).ToList();
                if (list.Count() > 0)
                {
                    DataValue result = null;
                    NodeId ni = null;
                    await ReadNodes(list, (r, i) => { result = r; ni = list[i]; }).ConfigureAwait(true);

                    if (StatusCode.IsGood(result.StatusCode))
                    {
                        toolStripTextBox2.Text = JsonConvert.SerializeObject(result.Value);
                        //if (result.Value is Array arr)
                        //{
                        //    toolStripTextBox2.Text = "[" + string.Join(",", arr.Cast<object>().Select(ar => ar is string ? $"\"{ar}\"" : ar.ToString().ToLower())) + "]";
                        //}
                        //else
                        //    toolStripTextBox2.Text = $"{result.Value}";
                    }
                    else
                    {
                        toolStripTextBox2.Text = $"Error:{result.StatusCode}";
                    }

                    var treenode = treeView1.Nodes.Find(ni.ToString(), true).FirstOrDefault();
                    if (treenode != null)
                    {
                        if (StatusCode.IsGood(result.StatusCode))
                        {
                            treenode.ToolTipText = JsonConvert.SerializeObject(result.Value);
                            //if (result.Value is Array arr)
                            //{
                            //    treenode.ToolTipText = "value："
                            //        + string.Join(",", arr.Cast<object>().Select(ar => ar.ToString()));
                            //}
                            //else
                            //    treenode.ToolTipText = "value：" + $"{result.Value}";
                        }
                        else
                        {
                            treenode.ToolTipText = "Errror：" + $"{result.StatusCode}";
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private async void 测试写入ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                var nn = treeView1.SelectedNode.Name;
                Type t = treeView1.SelectedNode.Tag as Type;
                if (t != null)
                {
                    //var va = Convert.ChangeType(toolStripTextBox2.Text, t);
                    var v = toolStripTextBox2.Text;
                    //if (t == typeof(string))
                    //{
                    //    v = "\"" + toolStripTextBox2.Text + "\"";
                    //}
                    var va = JsonConvert.DeserializeObject(v, t);
                    var nvs = new[] { nn }.Select(ni => (ni, v: va));
                    var response = await session.WriteAsync(null,
                        new WriteValueCollection(nvs.Select(ni => new WriteValue { NodeId = ni.ni, AttributeId = Attributes.Value, Value = new DataValue(new Variant(ni.v)) })), default).ConfigureAwait(true);

                    foreach (var result in response.Results)
                    {
                        if (StatusCode.IsGood(result))
                        {
                            MessageBox.Show(result.ToString(), "修改成功", MessageBoxButtons.OK, MessageBoxIcon.None);
                        }
                        else
                        {
                            MessageBox.Show(result.ToString(), "失败:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void 导出所选目录ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var nn = treeView1.SelectedNode;
            if (nn != null)
            {
                var list = new List<string>();
                Action<TreeNode> action = null;
                action = (TreeNode parent) =>
                {
                    for (int i = 0; i < parent.Nodes.Count; i++)
                    {
                        var thisNode = parent.Nodes[i];
                        list.Add(thisNode.Text);
                        action.Invoke(thisNode);
                    }
                };
                action(nn);
                SaveFileDialog saveFileDialog = new SaveFileDialog();
                saveFileDialog.RestoreDirectory = true;
                saveFileDialog.FileName = "导出" + nn.Text.Split('【').FirstOrDefault() + ".txt";
                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    System.IO.File.WriteAllText(saveFileDialog.FileName, string.Join("\r\n", list));
                };

            }
        }

    }
}
